/*
 *  Copyright 2010 Susanta Tewari.
 *  This file is part of genomemap.
 * 
 *  genomemap is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  genomemap is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with genomemap.  If not, see <http://www.gnu.org/licenses/>.
 */
package genomemap.queries;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import genomemap.io.DataIO;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @version 1.0 Jan 2, 2010
 * @author Susanta Tewari
 */
public class QueryRunner {

    /**
     * Use system properties data.properties=query.properties and
     * query.properties=query.properties. Property data.properties is used by package
     * genomemap.io and property query.properties is used to feed properties for
     * <code>QueryRunner</code>
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        QueryRunner runner = QueryRunner.getInstance();
        runner.run();

    }

    private static QueryRunner instance;

    private Configuration cfg = new Configuration();

    private Properties props = new Properties();

    private List<Query> queries = null;

    static final String RUN_QUERIES_PROP = "run.queries";

    static final String TEMPLATE_DIR_PROP = "templates.dir";

    static final String QUERY_OUTPUT_DIR_SUFFIX = ".output.dir";

    static final String QUERY_OUTPUT_FILE_SUFFIX = ".output.file";

    static final String QUERY_TEMPLATE_SUFFIX = ".template";

    {
        String propVal = null;
        try {
            propVal = System.getProperty("query.properties");
            props.load(DataIO.getInstance().getPlatformIO().
                    getInputStream(propVal));
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, ex.getMessage());
            throw new RuntimeException("Error in processing property query.properties = " + propVal);
        }
    }

    private QueryRunner() {

        // validate run.queries
        String runQueriesVal = getProperty(RUN_QUERIES_PROP);
        validateProperty(RUN_QUERIES_PROP, runQueriesVal);

        // load queries
        List<String> queryStrings = Arrays.asList(runQueriesVal.split(","));
        if (queryStrings.size() == 0)
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "No queries to run.");

        queries = new ArrayList<Query>();
        for (Query query : ServiceLoader.load(Query.class))
            if (queryStrings.contains(query.getClass().getSimpleName())) queries.add(query);


        // validate templates.dir
        String templatesDirVal = getProperty(TEMPLATE_DIR_PROP);
        validateProperty(TEMPLATE_DIR_PROP, templatesDirVal);
        validateDirProperty(TEMPLATE_DIR_PROP, templatesDirVal);

        // configure the freemarker Conifuration
        try {
            cfg.setDirectoryForTemplateLoading(new File(templatesDirVal));
        } catch (IOException ex) {
            Logger.getLogger(QueryRunner.class.getName()).log(Level.SEVERE, null, ex);
        }
        cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);


        // validate query properties for each running query
        validateQueryProperties();

    }

    public static QueryRunner getInstance() {

        if (instance == null) instance = new QueryRunner();
        return instance;
    }

    /**
     *
     * @throws IOException
     * @throws TemplateException
     */
    public void run() throws IOException, TemplateException {

        for (Query query : queries) {

            String queryName = query.getName();

            Writer out = new FileWriter(getProperty(queryName + QUERY_OUTPUT_DIR_SUFFIX) +
                    System.getProperty("file.separator") +
                    getProperty(queryName + QUERY_OUTPUT_FILE_SUFFIX));

            Template temp = cfg.getTemplate(getProperty(queryName + QUERY_TEMPLATE_SUFFIX));
            
            temp.process(query.getDataModel(), out);

            Logger.getLogger(getClass().getName()).log(Level.INFO,
                    "Query " + queryName + " has been processed.");

            out.close();
        }


    }

    public String getProperty(String key) {
        return props.getProperty(key);
    }

    /**
     * For all the queries about to run check if the required properties are set.
     * @throws RuntimeException
     */
    private void validateQueryProperties() {

        String templatesDir = getProperty(TEMPLATE_DIR_PROP);
        String fileSeparator = System.getProperty("file.separator");

        for (Query query : queries) {

            String queryName = query.getName();

            // .output.dir
            String outputDirProp = queryName + QUERY_OUTPUT_DIR_SUFFIX;
            String outputDirVal = getProperty(outputDirProp);

            validateProperty(outputDirProp, outputDirVal);
            validateDirProperty(outputDirProp, outputDirVal);

            // .output.file
            String outputFileProp = queryName + QUERY_OUTPUT_FILE_SUFFIX;
            String outputFileVal = getProperty(outputFileProp);

            validateProperty(outputFileProp, outputFileVal);

            //.template
            String templateFileProp = queryName + QUERY_TEMPLATE_SUFFIX;
            String templateFileVal = getProperty(templateFileProp);
            validateProperty(templateFileProp, templateFileVal);
            validateFileProperty(templateFileProp, templatesDir + fileSeparator + templateFileVal);

        }
    }

    /**
     * A property is deemed valid if it is defined and has a non-empty value.
     * @param prop
     * @return true if the property is validated else false
     * @throws RuntimeException
     */
    private void validateProperty(String prop, String value) {

        if (value == null || value.length() == 0)
            throw new RuntimeException("property " + prop + "=" + value);
    }

    /**
     * Checks if the directory property points to a valid directory
     * @param prop
     * @param value
     * @throws RuntimeException
     */
    private void validateDirProperty(String prop, String value) {

        File outputDir = new File(value);
        if (!outputDir.isDirectory())
            throw new RuntimeException(prop + "=" + value + " is not a valid directory");

    }

    /**
     * Checks if the file property points to an existing file
     * @param prop
     * @param value
     * @throws RuntimeException
     */
    private void validateFileProperty(String prop, String value) {

        File outputFile = new File(value);
        if (!outputFile.exists())
            throw new RuntimeException(prop + "=" + value + " is not an existing file");

    }

}
